import os
import logging
import traceback
from moviepy.video.io.VideoFileClip import VideoFileClip
from moviepy import concatenate_videoclips
from utils import get_video_info
from utils.download_utils import is_url, download_video, cleanup_downloaded_files
from typing import List, Tuple, Optional, Dict, Any

# 导入OSS工具
from utils.oss_utils import get_oss_uploader
import hashlib

# 配置日志
logger = logging.getLogger(__name__)


def _normalize_video_clip(clip, target_size, target_fps):
    """
    标准化视频剪辑的尺寸和帧率，保持宽高比不变
    
    Args:
        clip: 视频剪辑对象
        target_size: 目标尺寸 (width, height)
        target_fps: 目标帧率
        
    Returns:
        标准化后的视频剪辑
    """
    # 获取原始尺寸
    original_width, original_height = clip.size
    target_width, target_height = target_size
    
    # 计算缩放比例，保持宽高比不变
    width_ratio = target_width / original_width
    height_ratio = target_height / original_height
    
    # 选择较小的比例以确保视频完全适应目标尺寸
    scale_ratio = min(width_ratio, height_ratio)
    
    # 计算新的尺寸
    new_width = int(original_width * scale_ratio)
    new_height = int(original_height * scale_ratio)
    
    logger.info(f"调整视频尺寸: {original_width}x{original_height} -> {new_width}x{new_height}")
    
    # 调整尺寸
    resized_clip = clip.resized((new_width, new_height))
    
    # 如果帧率不同，调整帧率
    if abs(resized_clip.fps - target_fps) > 0.1:
        logger.info(f"调整视频帧率: {resized_clip.fps} -> {target_fps}")
        # MoviePy会自动处理帧率调整
    
    return resized_clip


def concat_processor(
    video_paths: List[str],
    target_duration: Optional[float] = None,
    output_path: Optional[str] = None,
    maintain_aspect_ratio: bool = True,
    target_size: Optional[Tuple[int, int]] = None,
    target_fps: Optional[float] = None,
    task_id: Optional[str] = None,
    oss_bucket_dir: str = "freefishpc/cloud_video/dispose"
) -> Tuple[str, Dict[str, Any]]:
    """
    增强版视频拼接处理器，支持处理不同分辨率和帧率的视频
    
    Args:
        video_paths: 视频文件路径列表（支持本地路径和网络URL）
        target_duration: 目标时长（秒），如果为None则使用所有视频的总时长
        output_path: 输出文件路径，如果为None则自动生成
        maintain_aspect_ratio: 是否保持宽高比不变
        target_size: 目标尺寸 (width, height)，如果为None则使用最大视频的尺寸
        target_fps: 目标帧率，如果为None则使用最高帧率
        task_id: 任务ID，用于生成唯一的输出文件名
        oss_bucket_dir: OSS存储桶中的目录路径
        
    Returns:
        Tuple[str, Dict[str, Any]]: (输出文件路径或OSS链接, 视频信息字典)
    """
    logger.info(f"开始处理视频拼接，视频数量: {len(video_paths)}")
    
    if not video_paths:
        raise ValueError("视频路径列表不能为空")
    
    # 使用项目目录下的download文件夹
    project_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    download_dir = os.path.join(project_dir, "download")
    downloaded_files = []
    
    try:
        # 处理视频路径，下载网络链接
        local_video_paths = []
        for i, path in enumerate(video_paths):
            if is_url(path):
                logger.info(f"检测到网络链接，开始下载视频 {i+1}/{len(video_paths)}: {path}")
                try:
                    local_path = download_video(path, download_dir)
                    local_video_paths.append(local_path)
                    downloaded_files.append(local_path)
                    logger.info(f"视频下载完成: {path} -> {local_path}")
                except Exception as e:
                    logger.warning(f"下载视频失败，忽略该记录: {path} - {e}")
                    continue  # 忽略下载失败的记录
            else:
                local_video_paths.append(path)
        
        # 加载所有视频片段并收集信息
        clips = []
        total_duration = 0.0
        max_width = 0
        max_height = 0
        max_fps = 0.0
        
        # 第一遍：加载视频并收集尺寸和帧率信息
        valid_video_paths = []
        max_pixels = 0
        max_video_width = 0
        max_video_height = 0
        for i, path in enumerate(local_video_paths):
            logger.info(f"加载视频 {i+1}/{len(local_video_paths)}: {path}")
            try:
                clip = VideoFileClip(path)
                clips.append(clip)
                valid_video_paths.append(path)
                total_duration += clip.duration
                logger.info(f"视频 {i+1} 时长: {clip.duration} 秒, 尺寸: {clip.size}, 帧率: {clip.fps}")
                
                # 更新最大尺寸和帧率
                width, height = clip.size
                max_width = max(max_width, width)
                max_height = max(max_height, height)
                max_fps = max(max_fps, clip.fps)
                
                # 记录最大视频的实际宽高（按照像素总数计算）
                current_pixels = width * height
                if current_pixels > max_pixels:
                    max_pixels = current_pixels
                    max_video_width = width
                    max_video_height = height
            except Exception as e:
                logger.warning(f"加载视频失败 {path}: {e}")
                # 如果是下载的文件，记录下来以便清理
                if path in downloaded_files:
                    downloaded_files.remove(path)
                continue
        
        logger.info(f"所有视频加载完成，总时长: {total_duration} 秒")
        logger.info(f"最大尺寸: {max_width}x{max_height}, 最高帧率: {max_fps}")
        logger.info(f"尺寸最大的视频实际尺寸: {max_video_width}x{max_video_height}")
        
        # 检查是否有有效视频
        if not clips:
            logger.error("没有有效的视频文件可以处理")
            # 清理下载的文件
            cleanup_result = cleanup_downloaded_files(downloaded_files)
            if cleanup_result["failed"]:
                logger.warning(f"清理下载文件失败: {cleanup_result['failed']}")
            raise ValueError("没有有效的视频文件可以处理")
        
        # 确定目标尺寸和帧率
        if target_size is None:
            # 检查所有视频的分辨率是否一致
            all_sizes_same = True
            first_clip_size = clips[0].size if clips else None
            for clip in clips:
                if clip.size != first_clip_size:
                    all_sizes_same = False
                    break
            
            if all_sizes_same and first_clip_size:
                # 所有视频分辨率一致，使用该分辨率
                target_size = first_clip_size
                logger.info(f"所有视频分辨率一致: {target_size[0]}x{target_size[1]}，将使用此分辨率进行处理")
            else:
                # 检查是否需要限制分辨率（大于1080*1920或1920*1080）
                # 使用尺寸最大的视频的实际宽高来判断方向
                if max_video_width > 1920 or max_video_height > 1080:
                    # 根据视频的宽高比决定使用横屏还是竖屏目标尺寸
                    if max_video_width > max_video_height:
                        # 横屏视频，设置为1920x1080
                        target_size = (1920, 1080)
                        logger.info(f"尺寸最大的视频分辨率 {max_video_width}x{max_video_height} 超过了限制，检测为横屏视频，将目标尺寸设置为 1920x1080")
                    else:
                        # 竖屏视频，设置为1080x1920
                        target_size = (1080, 1920)
                        logger.info(f"尺寸最大的视频分辨率 {max_video_width}x{max_video_height} 超过了限制，检测为竖屏视频，将目标尺寸设置为 1080x1920")
                else:
                    target_size = (max_width, max_height)
        
        if target_fps is None:
            target_fps = max_fps
        
        logger.info(f"目标尺寸: {target_size}, 目标帧率: {target_fps}")
        
        # 第二遍：标准化所有视频剪辑
        normalized_clips = []
        if maintain_aspect_ratio:
            logger.info("保持宽高比不变，调整视频尺寸...")
            for i, clip in enumerate(clips):
                try:
                    normalized_clip = _normalize_video_clip(clip, target_size, target_fps)
                    normalized_clips.append(normalized_clip)
                except Exception as e:
                    logger.warning(f"标准化视频失败 {valid_video_paths[i]}: {e}")
                    clip.close()  # 关闭原始clip
                    continue
        else:
            # 不保持宽高比，直接调整到目标尺寸
            logger.info("不保持宽高比，直接调整到目标尺寸...")
            for i, clip in enumerate(clips):
                try:
                    resized_clip = clip.resized(target_size)
                    normalized_clips.append(resized_clip)
                except Exception as e:
                    logger.warning(f"调整视频尺寸失败 {valid_video_paths[i]}: {e}")
                    clip.close()  # 关闭原始clip
                    continue
        
        # 检查是否有有效视频
        if not normalized_clips:
            logger.error("没有有效的视频剪辑可以处理")
            raise ValueError("没有有效的视频剪辑可以处理")
        
        # 拼接视频
        logger.info("开始拼接视频...")
        try:
            final_clip = concatenate_videoclips(normalized_clips, method="compose")
        except Exception as e:
            logger.error(f"视频拼接失败: {e}")
            # 关闭所有clip以释放资源
            for clip in normalized_clips:
                clip.close()
            raise Exception(f"视频拼接失败: {e}")
        
        # 如果指定了目标时长，则截取视频
        if target_duration is not None:
            try:
                if target_duration > final_clip.duration:
                    logger.warning(f"目标时长({target_duration}s)超过视频总时长({final_clip.duration}s)，将使用视频总时长")
                else:
                    logger.info(f"截取视频到 {target_duration} 秒")
                    final_clip = final_clip.subclipped(0, target_duration)
            except Exception as e:
                logger.warning(f"视频截取时出现问题: {e}")
                # 继续使用完整视频
        
        # 生成输出文件路径
        if output_path is None:
            # 设置默认输出目录为output
            output_dir = "output"
            # 确保输出目录存在
            if not os.path.exists(output_dir):
                os.makedirs(output_dir)
            # 生成带task_id的输出文件名
            if task_id:
                output_path = os.path.join(output_dir, f"output_{task_id}_{int(total_duration)}s.mp4")
            else:
                # 如果没有提供task_id，使用时间戳
                import datetime
                current_time = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
                output_path = os.path.join(output_dir, f"output_{current_time}_{int(total_duration)}s.mp4")
        
        # 确保输出目录存在
        output_dir = os.path.dirname(output_path)
        if output_dir and not os.path.exists(output_dir):
            os.makedirs(output_dir)
        
        # 导出视频
        logger.info(f"导出视频到: {output_path}")
        final_clip.write_videofile(
            output_path,
            codec='libx264',
            audio_codec='aac',
            temp_audiofile='temp-audio.m4a',
            remove_temp=True,
            logger=None,
            fps=target_fps
        )
        
        # 关闭所有clip以释放资源
        final_clip.close()
        for clip in normalized_clips:
            clip.close()
        for clip in clips:
            clip.close()
        
        # 获取视频信息
        video_info = get_video_info(output_path)
        
        # 上传到OSS
        try:
            logger.info("开始上传视频到OSS")
            # 创建OSS上传器（自动使用项目配置的环境）
            oss_uploader = get_oss_uploader()
            
            if oss_uploader:
                # 使用task_id作为OSS对象键的一部分，如果没有task_id则使用时间戳
                if task_id:
                    oss_object_key = f"{oss_bucket_dir}/{task_id}.mp4"
                else:
                    # 如果没有提供task_id，使用时间戳
                    import datetime
                    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
                    oss_object_key = f"{oss_bucket_dir}/concat_{timestamp}.mp4"
                
                # 上传文件到OSS
                oss_url = oss_uploader.upload_file(output_path, oss_object_key)
                logger.info(f"视频已上传到OSS: {oss_url}")
                
                # 更新返回的路径为OSS链接
                final_output_path = oss_url
            else:
                logger.warning("无法创建OSS上传器，返回本地文件路径")
                final_output_path = output_path
        except Exception as e:
            logger.error(f"上传视频到OSS失败: {e}")
            # 如果上传失败，仍然返回本地文件路径
            final_output_path = output_path
        
        # 清理下载的文件
        if downloaded_files:
            cleanup_result = cleanup_downloaded_files(downloaded_files)
            if cleanup_result["failed"]:
                logger.warning(f"清理下载文件失败: {cleanup_result['failed']}")
            
            # 不再删除download目录，只删除本次任务下载的文件
            logger.info(f"已清理本次任务下载的文件，保留下载目录: {download_dir}")
        
        logger.info("视频处理完成")
        return final_output_path, video_info
        
    except Exception as e:
        # 清理下载的文件
        if downloaded_files:
            cleanup_result = cleanup_downloaded_files(downloaded_files)
            if cleanup_result["failed"]:
                logger.warning(f"清理下载文件失败: {cleanup_result['failed']}")
            
            # 不再删除download目录，只删除本次任务下载的文件
            logger.info(f"已清理本次任务下载的文件，保留下载目录: {download_dir}")
        
        # 关闭所有clip以释放资源
        try:
            for clip in clips:
                clip.close()
        except:
            pass
            
        logger.error(f"处理视频时发生错误: {e}")
        logger.error(traceback.format_exc())
        raise

